Upgrade ONNX runtime, ONNX Gen AI runtime, add a new model to test changes#1085
Upgrade ONNX runtime, ONNX Gen AI runtime, add a new model to test changes#1085ammbra wants to merge 18 commits into
Conversation
Signed-off-by: Ana-Maria Mihalceanu <ana-maria.m.mihalceanu@oracle.com>
|
👋 Welcome back amihalceanu! A progress list of the required criteria for merging this PR into |
|
@ammbra This change now passes all automated pre-integration checks. ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details. After integration, the commit message for the final commit will be: You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed. At the time when this comment was updated there had been 15 new commits pushed to the
As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details. As you do not have Committer status in this project an existing Committer must agree to sponsor your change. Possible candidates are the reviewers of this PR (@asotona) but any other Committer may sponsor as well. ➡️ To flag this PR as ready for integration with the above commit message, type |
Webrevs
|
Signed-off-by: Ana-Maria Mihalceanu <ana-maria.m.mihalceanu@oracle.com>
Signed-off-by: Ana-Maria Mihalceanu <ana-maria.m.mihalceanu@oracle.com>
Signed-off-by: Ana-Maria Mihalceanu <ana-maria.m.mihalceanu@oracle.com>
Signed-off-by: Ana-Maria Mihalceanu <ana-maria.m.mihalceanu@oracle.com>
There was a problem hiding this comment.
I’m still working through all the proposal, so please correct me if I misunderstand something.
There is clearly a need for named inputs and outputs in the graph. This is already handled inside OnnxTransformer, where ModuleAndInitializers holds the name map, allowing method parameter names and return record component names to be forwarded to the ONNX model.
The proposal seems to go in the opposite direction: to apply names parsed from the config file. I’m a bit concerned about this, because it moves us away from the idea that Java should remain the source of truth for the model.
There is also a clear need to provide shape information for some ONNX operations. This is new and would require passing additional information into the protobuf-building process. There are two parts to this issue:
- identify the op input that requires an explicit shape
- provide the actual shape value
If I understand the proposal correctly, the tensor would be identified by its name coming from the config, and the shape value would be represented as a list of objects, mixing literal values with references to the configuration file or even references to internal runtime values. My concern is that none of this information comes directly from the Java source.
If the shape definitions are primarily needed for method parameters, then the mapping key should probably be the tensor method parameter, or more generally some code-reflection value. If we had code reflection support for value attributes, or if we had support for parameter annotations, this could be handled more cleanly. Since we are not there yet, some workaround may be necessary. For example using parameter names as keys for the tensor shape resolver.
For the second part (the shape itself) I think it should still be Java-sourced. In practice, that would mean resolving it to an array of longs. Otherwise we risk introducing unclear references to “magic” constants coming from external configuration.
When I refer to shape inference, I mean that if shape information is required deeper in the model tree, it should ideally be inferred from the parameters and weights through the operations in the tree. Since that is a more complex task, we could temporarily rely on a Java variable-name-based mapping, even for values deeper in the model. Another option would be to identify the ops that require shape information and add synthetic shape attributes to them, in addition to their specs-generated attributes, so the shape constants are still passed explicitly from Java.
Signed-off-by: Ana-Maria Mihalceanu <ana-maria.m.mihalceanu@oracle.com>
Signed-off-by: Ana-Maria Mihalceanu <ana-maria.m.mihalceanu@oracle.com>
Signed-off-by: Ana-Maria Mihalceanu <ana-maria.m.mihalceanu@oracle.com>
Signed-off-by: Ana-Maria Mihalceanu <ana-maria.m.mihalceanu@oracle.com>
Signed-off-by: Ana-Maria Mihalceanu <ana-maria.m.mihalceanu@oracle.com>
asotona
left a comment
There was a problem hiding this comment.
Nice work!
I’m glad there’s a clean workaround that replaces the shape hack.
Just a few minor comments below.
…ens. Signed-off-by: Ana-Maria Mihalceanu <ana-maria.m.mihalceanu@oracle.com>
Signed-off-by: Ana-Maria Mihalceanu <ana-maria.m.mihalceanu@oracle.com>
Signed-off-by: Ana-Maria Mihalceanu <ana-maria.m.mihalceanu@oracle.com>
Signed-off-by: Ana-Maria Mihalceanu <ana-maria.m.mihalceanu@oracle.com>
Signed-off-by: Ana-Maria Mihalceanu <ana-maria.m.mihalceanu@oracle.com>
|
Thank you for your review, Adam. While addressing the issues noticed by you, I observed that the "fixed" model is slow. I was intrigued and looked into the model card example and noticed this for generating a response: const output = await generator(messages, { max_new_tokens: 128 });I further searched what that
That quote matches the slow model at runtime behavior, once I introduced KV cache for For the moment, I added the Otherwise, if current state of the implementation is ok, please help me integrate the changes. |
asotona
left a comment
There was a problem hiding this comment.
I don't think creating a fresh generator in prompt is OK. It bypasses the session generator and drops the context between prompts.
maxNewTokens should be configured on the session generator
| return switch (this) { | ||
| case INT4, UINT4, FLOAT4E2M1 -> 4; | ||
| case UINT8, INT8, BOOL, FLOAT8E4M3FN, FLOAT8E4M3FNUZ, FLOAT8E5M2, FLOAT8E5M2FNUZ -> 8; | ||
| case INT2, INT4, UINT2, UINT4, FLOAT4E2M1 -> 4; |
There was a problem hiding this comment.
I think INT2 and UINT2 should have bit size 2, not 4.
There was a problem hiding this comment.
You are right. I misread https://onnx.ai/onnx/technical/int2.html.
| public void prompt(String prompt, int maxNewTokens, Consumer<String> outputConsumer) { | ||
| if (maxNewTokens < 0) { | ||
| throw new IllegalArgumentException("maxNewTokens must be above 0, found " + maxNewTokens); | ||
| } | ||
| LOG.log(System.Logger.Level.DEBUG, "Create sequences"); | ||
| var inputTokens = call(OgaCreateSequences(ret)); | ||
|
|
||
| MemorySegment localGeneratorParams = MemorySegment.NULL; | ||
| MemorySegment localGenerator = MemorySegment.NULL; | ||
| try { | ||
| call(OgaTokenizerEncode(tokenizer, arena.allocateFrom(prompt), inputTokens)); | ||
| LOG.log(System.Logger.Level.DEBUG, "Tokenizer encode"); | ||
| long promptTokens = OgaSequencesGetSequenceCount(inputTokens, 0L); | ||
| long maxLength = Math.addExact(promptTokens, maxNewTokens); | ||
| localGeneratorParams = call(OgaCreateGeneratorParams(model, ret)); | ||
| call(OgaGeneratorParamsSetSearchNumber(localGeneratorParams, arena.allocateFrom("max_length"), maxLength)); | ||
| localGenerator = call(OgaCreateGenerator(model, localGeneratorParams, ret)); | ||
|
|
||
| generate(inputTokens, localGenerator, outputConsumer); | ||
| } finally { | ||
| if (!localGeneratorParams.equals(MemorySegment.NULL)) | ||
| OgaDestroyGeneratorParams(localGeneratorParams); | ||
| if (!localGenerator.equals(MemorySegment.NULL)) | ||
| OgaDestroyGenerator(localGenerator); |
There was a problem hiding this comment.
This looks like a workaround around the existing generator creation.
There was a problem hiding this comment.
It is....I moved this logic in the main prompt generation, but this means that generator is no longer final and initialized with MemorySegment.NULL. generator depends on inputTokens (maxLength = promptTokens + maxNewTokens).
There was a problem hiding this comment.
With a new generator the session loses context with each prompt. This makes the Llama session behave a bit like it has Alzheimer’s.
There was a problem hiding this comment.
In that implementation...yes. So...I reverted the changes done there because what I actually did was to set the generator to use a max_length of prompt_length + 128. I did alter the genai_config.json max_length and is now set it for a lower value.
When max_length = context_length, the loop may continue much longer. As more tokens are generated (based on max_length = context_length), the KV cache grows and potentially that makes the model slower. I did try to set another provider (CoreML in my case) but the model is equally slow. Can you please take a look at the reverted changes and also the value set for max_length?
Signed-off-by: Ana-Maria Mihalceanu <ana-maria.m.mihalceanu@oracle.com>
…other changes. Signed-off-by: Ana-Maria Mihalceanu <ana-maria.m.mihalceanu@oracle.com>
| private final MemorySegment tokenizer; | ||
| private final MemorySegment tokenizerStream; | ||
| private final MemorySegment generatorParams; | ||
| private MemorySegment generator; |
There was a problem hiding this comment.
Could we make this field final again?
There was a problem hiding this comment.
Yes, done. Thank you for catching this.
Signed-off-by: Ana-Maria Mihalceanu <ana-maria.m.mihalceanu@oracle.com>
|
Thank you for all the time spent on this. Please |
|
/sponsor |
|
Going to push as commit e109b0b.
Your commit was automatically rebased without conflicts. |
I apologize for such a big PR, but most of its changes are result of regeneration for native bindings, operators, prototufs etc. So, this PR contains many changes that relate to upgrading the ONNX runtime library (1.26.0) and its native GenAI (0.14.0) counterpart:
oracle.code.onnx.foreignare generated via the cr-examples/onnx/opgen/setup.sh script and its helper symbols files to generate only the absolute necessary contents.OnnxProtoBuilderneeded to support also shapes because I kept getting this native error once rebuilding Babylon:I managed to identify that the exported model was the source for such an error (both local debug and by also comparing in Netron). To my understanding,
Generators::Model::IsPruned() constmethod checks if the underlying model has been compressed using model pruning. Pruning seems to reduce model size and latency by zeroing out or permanently removing unnecessary weights and parameters, but I think in this case the generated model was aggressively pruned from the beginning of traversing the graph, thus resulting in the error above. The aggressive pruning was performed due the lack of shapes being there. This error occurred only for LLMs, the rest of the ML models being unaffected.LlamaModelto useReshapein order to fix tensor rank/shape visibility as the underlying tensor layout is already correct.AllMiniLML6V2EmbeddingModelis inspired by https://huggingface.co/onnx-community/all-MiniLM-L6-v2-ONNX and by running theOnnxLiftover its default (model.onnx). As a result, I could obtain a quite good model to start. Then I cleaned it locally based on how the official config.json looked like. TheBertTokenizeris just a helper for working with the tokenizer and its constants are from tokenizer_config.json and config.json , while theEmbeddingDemois just a Java version of the code from the landing page of the model.Progress
Reviewers
Reviewing
Using
gitCheckout this PR locally:
$ git fetch https://git.openjdk.org/babylon.git pull/1085/head:pull/1085$ git checkout pull/1085Update a local copy of the PR:
$ git checkout pull/1085$ git pull https://git.openjdk.org/babylon.git pull/1085/headUsing Skara CLI tools
Checkout this PR locally:
$ git pr checkout 1085View PR using the GUI difftool:
$ git pr show -t 1085Using diff file
Download this PR as a diff file:
https://git.openjdk.org/babylon/pull/1085.diff
Using Webrev
Link to Webrev Comment